package qw

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"net/url"
	"strconv"
	"time"

	"gitee.com/gxcc/go-utils/utils"

	"gitee.com/gxcc/go-thirds/cache"
)

// QYWXApi 企业微信API
type QYWXApi struct {
	CorpId          string
	AgentId         string
	AppSecret       string
	ProxyProtocol   string
	ProxyServerHost string
	ProxyServerPort int

	httpProxySvc *utils.HttpProxySvc
}

func (w *QYWXApi) getHttpSvc() *utils.HttpProxySvc {
	var proxyServerUrl = ""
	if len(w.ProxyProtocol) > 0 && len(w.ProxyServerHost) > 0 && w.ProxyServerPort > 0 {
		proxyServerUrl = w.ProxyProtocol + "://" + w.ProxyServerHost + ":" + strconv.Itoa(w.ProxyServerPort)
	}
	slog.Debug("use http proxy", "proxyServerUrl", proxyServerUrl)

	if w.httpProxySvc == nil {
		w.httpProxySvc = &utils.HttpProxySvc{ProxyServerUrl: proxyServerUrl}
	}

	return w.httpProxySvc
}

func (w *QYWXApi) getAccessTokenCacheKey() string {
	return "qywx_access_token_" + w.AgentId + "_" + w.CorpId
}

// 刷新AccessToken
func (w *QYWXApi) reloadAccessToken() (string, error) {
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + w.CorpId + "&corpsecret=" + w.AppSecret
	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	type Info struct {
		AccessToken string `json:"access_token"`
		ExpiresIn   int    `json:"expires_in"`
	}
	var info Info
	if err := json.Unmarshal(res.Body, &info); err != nil {
		slog.Error(err.Error())
		return "", err
	}
	if len(info.AccessToken) == 0 {
		return "", errors.New("获取accessToken失败，返回结果：" + string(res.Body))
	}

	// 缓存到redis中
	var ctx = context.Background()
	var cacheKey = w.getAccessTokenCacheKey()
	cache.RedisClient.Set(ctx, cacheKey, info.AccessToken, time.Duration(info.ExpiresIn-60)*time.Second)

	slog.Debug("reloadAccessToken", "accessToken", info.AccessToken)
	return info.AccessToken, nil
}

// GetAccessToken 获取AccessToken
func (w *QYWXApi) GetAccessToken() (string, error) {
	if len(w.CorpId) == 0 || len(w.AppSecret) == 0 {
		return "", errors.New("CorpId和CorpSecret为空")
	}

	var ctx = context.Background()
	var cacheKey = w.getAccessTokenCacheKey()
	accessToken, err := cache.RedisClient.Get(ctx, cacheKey).Result()
	if err != nil || len(accessToken) == 0 {
		accessToken, err := w.reloadAccessToken()
		if err != nil {
			slog.Error(err.Error())
			return "", err
		} else {
			return accessToken, nil
		}
	} else {
		return accessToken, nil
	}
}

// SetAccessToken 设置AccessToken
func (w *QYWXApi) SetAccessToken(accessToken string, expiresIn time.Duration) {
	// 缓存到redis中
	var ctx = context.Background()
	var cacheKey = w.getAccessTokenCacheKey()
	cache.RedisClient.Set(ctx, cacheKey, accessToken, expiresIn)
}

// GetAuthUser 获取认证用户
func (w *QYWXApi) GetAuthUser(code string) (UserAuthResult, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return UserAuthResult{}, err
	}

	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=" + accessToken + "&code=" + code

	// 调试
	//url += "&debug=1"

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return UserAuthResult{}, err
	}

	// 调试日志
	slog.Debug("GetUserInfo", "response", string(res.Body))

	var errorResult ErrorResult
	if err := json.Unmarshal(res.Body, &errorResult); err != nil {
		slog.Error(err.Error())
		return UserAuthResult{}, err
	}
	if errorResult.Errcode != 0 {
		return UserAuthResult{}, errors.New("获取用户信息失败，返回结果：" + string(res.Body))
	}

	var result UserAuthResult
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return UserAuthResult{}, err
	}
	return result, nil
}

// GetAuthUserDetail 获取认证用户详细信息
func (w *QYWXApi) GetAuthUserDetail(userTicket string) (UserDetailResult, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return UserDetailResult{}, err
	}

	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=" + accessToken
	// reqUrl += "&debug=1" // 调试

	var bodyMap = map[string]any{
		"user_ticket": userTicket,
	}
	bodyData, _ := json.Marshal(bodyMap)

	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, bodyData)
	if err != nil {
		slog.Error(err.Error())
		return UserDetailResult{}, err
	}

	// 调试日志
	slog.Debug("GetUserInfo", "response", string(res.Body))

	var errorResult ErrorResult
	if err := json.Unmarshal(res.Body, &errorResult); err != nil {
		slog.Error(err.Error())
		return UserDetailResult{}, err
	}
	if errorResult.Errcode != 0 {
		return UserDetailResult{}, errors.New("获取用户信息失败，返回结果：" + string(res.Body))
	}

	var result UserDetailResult
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return UserDetailResult{}, err
	}
	return result, nil
}

// GetDeptList 获取部门列表(接口已被禁用)
// id: 部门id。获取指定部门及其下的子部门（以及及子部门的子部门等等，递归）。 如果不填，默认获取全量组织架构
func (w *QYWXApi) GetDeptList(id int64) ([]Department, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=" + accessToken
	if id != 0 {
		reqUrl += "&id=" + strconv.FormatInt(id, 10)
	}

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetDeptList", "response", string(res.Body))

	type Result struct {
		Errcode    int          `json:"errcode"`
		Errmsg     string       `json:"errmsg"`
		Department []Department `json:"department"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取部门列表失败，返回结果：" + string(res.Body))
	}
	return result.Department, nil
}

// GetDeptSimpleList 获取部门列表
// id: 部门id。获取指定部门及其下的子部门（以及及子部门的子部门等等，递归）。 如果不填，默认获取全量组织架构
func (w *QYWXApi) GetDeptSimpleList(id int64) ([]DepartmentId, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token=" + accessToken
	if id != 0 {
		reqUrl += "&id=" + strconv.FormatInt(id, 10)
	}

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetDeptSimpleList", "response", string(res.Body))

	type Result struct {
		Errcode      int            `json:"errcode"`
		Errmsg       string         `json:"errmsg"`
		DepartmentId []DepartmentId `json:"department_id"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取部门列表失败，返回结果：" + string(res.Body))
	}
	return result.DepartmentId, nil
}

// GetDepartment 获取部门详情
// id: 部门id
func (w *QYWXApi) GetDepartment(id int64) (Department, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return Department{}, err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/get?access_token=" + accessToken
	if id != 0 {
		reqUrl += "&id=" + strconv.FormatInt(id, 10)
	}

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return Department{}, err
	}

	// 调试日志
	slog.Debug("GetDepartment:\n" + string(res.Body))

	type Result struct {
		Errcode    int        `json:"errcode"`
		Errmsg     string     `json:"errmsg"`
		Department Department `json:"department"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return Department{}, err
	}
	if result.Errcode != 0 {
		return Department{}, errors.New("获取部门详情失败，返回结果：" + string(res.Body))
	}
	return result.Department, nil
}

// CreateDept 创建部门
func (w *QYWXApi) CreateDept(name, nameEn string, parentId int64, orderNum int) (int64, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return 0, err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token=" + accessToken

	var params = map[string]any{
		"name":     name,
		"name_en":  nameEn,
		"parentid": parentId,
		"order":    orderNum,
	}
	reqBody, _ := utils.JsonMarshal(params)
	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, reqBody)
	if err != nil {
		slog.Error(err.Error())
		return 0, err
	}

	// 调试日志
	slog.Debug("CreateDept:\n" + string(res.Body))

	type Result struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
		Id      int64  `json:"id"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return 0, err
	}
	if result.Errcode != 0 {
		return 0, errors.New("创建部门失败，返回结果：" + string(res.Body))
	}
	return result.Id, nil
}

// UpdateDept 更新部门
func (w *QYWXApi) UpdateDept(id int64, name, nameEn string, orderNum int) error {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/update?access_token=" + accessToken

	var params = map[string]any{
		"id":      id,
		"name":    name,
		"name_en": nameEn,
		"order":   orderNum,
	}
	reqBody, _ := utils.JsonMarshal(params)
	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, reqBody)
	if err != nil {
		slog.Error(err.Error())
		return err
	}

	// 调试日志
	slog.Debug("UpdateDept:\n" + string(res.Body))

	type Result struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return err
	}
	if result.Errcode != 0 {
		return errors.New("更新部门失败，返回结果：" + string(res.Body))
	}
	return nil
}

// DeleteDept 删除部门
func (w *QYWXApi) DeleteDept(id int64) error {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/department/delete?access_token=" + accessToken
	if id != 0 {
		reqUrl += "&id=" + strconv.FormatInt(id, 10)
	}
	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return err
	}

	// 调试日志
	slog.Debug("DeleteDept:\n" + string(res.Body))

	type Result struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return err
	}
	if result.Errcode != 0 {
		return errors.New("删除部门失败，返回结果：" + string(res.Body))
	}
	return nil
}

// GetUserInfo 获取用户信息
func (w *QYWXApi) GetUserInfo(userid string) (User, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return User{}, err
	}
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=" + accessToken + "&userid=" + userid

	// 调试
	//url += "&debug=1"

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return User{}, err
	}

	// 调试日志
	slog.Debug("GetUserInfo:\n" + string(res.Body))

	var errorResult ErrorResult
	if err := json.Unmarshal(res.Body, &errorResult); err != nil {
		slog.Error(err.Error())
		return User{}, err
	}
	if errorResult.Errcode != 0 {
		return User{}, errors.New("获取用户信息失败，返回结果：" + string(res.Body))
	}

	var user User
	if err := json.Unmarshal(res.Body, &user); err != nil {
		slog.Error(err.Error())
		return User{}, err
	}
	return user, nil
}

// GetUserSimpleList 获取部门成员
// departmentId: 获取的部门id
// fetchChild: 是否递归获取子部门下面的成员：1-递归获取，0-只获取本部门
func (w *QYWXApi) GetUserSimpleList(departmentId int64, fetchChild int) ([]UserSimple, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	values.Add("department_id", fmt.Sprint(departmentId))
	values.Add("fetch_child", fmt.Sprint(fetchChild))
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?" + values.Encode()

	// 调试
	//url += "&debug=1"

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetUserSimpleList:\n" + string(res.Body))

	type Result struct {
		Errcode  int          `json:"errcode"`
		Errmsg   string       `json:"errmsg"`
		Userlist []UserSimple `json:"userlist"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取部门成员失败，返回结果：" + string(res.Body))
	}
	return result.Userlist, nil
}

// GetUserList 获取部门成员详情
// departmentId: 获取的部门id
// fetchChild: 是否递归获取子部门下面的成员：1-递归获取，0-只获取本部门
func (w *QYWXApi) GetUserList(departmentId int64, fetchChild int) ([]User, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	values.Add("department_id", fmt.Sprint(departmentId))
	values.Add("fetch_child", fmt.Sprint(fetchChild))
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/list?" + values.Encode()

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetUserList:\n" + string(res.Body))

	type Result struct {
		Errcode  int    `json:"errcode"`
		Errmsg   string `json:"errmsg"`
		Userlist []User `json:"userlist"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取部门成员失败，返回结果：" + string(res.Body))
	}
	return result.Userlist, nil
}

// GetUserIdList 获取成员ID列表(返回userId)
// cursor: 上一次调用时返回的next_cursor，第一次拉取可以不填
// limit: 每次拉取的数据量，默认值和最大值都为1000
func (w *QYWXApi) GetUserIdList(cursor string, limit int) ([]string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	var params = map[string]any{
		"limit": limit,
	}
	if len(cursor) > 0 {
		params["cursor"] = cursor
	}
	reqBody, _ := json.Marshal(params)

	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/list_id?" + values.Encode()

	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, reqBody)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetUserIdList:\n" + string(res.Body))

	type Result struct {
		Errcode    int      `json:"errcode"`
		Errmsg     string   `json:"errmsg"`
		NextCursor string   `json:"next_cursor"`
		DeptUser   []UserId `json:"dept_user"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取授权成员详情失败，返回结果：" + string(res.Body))
	}

	var userIdList []string
	for _, item := range result.DeptUser {
		userIdList = append(userIdList, item.UserId)
	}

	if len(result.NextCursor) > 0 && len(result.DeptUser) < limit {
		nextList, _ := w.GetUserAuthList(result.NextCursor, limit)
		if len(nextList) > 0 {
			userIdList = append(userIdList, nextList...)
		}
	}

	return userIdList, nil
}

// GetUserAuthList 获取成员授权列表(返回openUserId)
// cursor: 上一次调用时返回的next_cursor，第一次拉取可以不填
// limit: 每次拉取的数据量，默认值和最大值都为1000
func (w *QYWXApi) GetUserAuthList(cursor string, limit int) ([]string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return nil, err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	var params = map[string]any{
		"limit": limit,
	}
	if len(cursor) > 0 {
		params["cursor"] = cursor
	}
	reqBody, _ := json.Marshal(params)

	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/list_member_auth?" + values.Encode()

	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, reqBody)
	if err != nil {
		slog.Error(err.Error())
		return nil, err
	}

	// 调试日志
	slog.Debug("GetUserAuthList:\n" + string(res.Body))

	type Result struct {
		Errcode        int    `json:"errcode"`
		Errmsg         string `json:"errmsg"`
		NextCursor     string `json:"next_cursor"`
		MemberAuthList []struct {
			OpenUserid string `json:"open_userid"`
		} `json:"member_auth_list"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return nil, err
	}
	if result.Errcode != 0 {
		return nil, errors.New("获取授权成员详情失败，返回结果：" + string(res.Body))
	}

	var openUserIdList []string
	for _, item := range result.MemberAuthList {
		openUserIdList = append(openUserIdList, item.OpenUserid)
	}

	if len(result.NextCursor) > 0 && len(result.MemberAuthList) < limit {
		nextList, _ := w.GetUserAuthList(result.NextCursor, limit)
		if len(nextList) > 0 {
			openUserIdList = append(openUserIdList, nextList...)
		}
	}

	return openUserIdList, nil
}

// ConvertUserIdoOpenId userid转openid
// userid: 企业内的成员id
// 该接口使用场景为企业支付，在使用企业红包和向员工付款时，需要自行将企业微信的userid转成openid。
// 注：需要成员使用微信登录企业微信或者关注微工作台（原企业号）才能转成openid;
// 如果是外部联系人，请使用外部联系人openid转换转换openid
func (w *QYWXApi) ConvertUserIdoOpenId(userid string) (string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?" + values.Encode()

	// 调试
	//url += "&debug=1"

	var params = map[string]any{
		"userid": userid,
	}
	data, _ := json.Marshal(params)

	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, data)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	// 调试日志
	slog.Debug("ConvertUserIdoOpenId:\n" + string(res.Body))

	type Result struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
		Openid  string `json:"openid"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return "", err
	}
	if result.Errcode != 0 {
		return "", errors.New("userid转openid失败，返回结果：" + string(res.Body))
	}
	return result.Openid, nil
}

// ConvertOpenIdToUserId openid转userid
func (w *QYWXApi) ConvertOpenIdToUserId(openid string) (string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid?" + values.Encode()

	var params = map[string]any{
		"openid": openid,
	}
	data, _ := json.Marshal(params)

	res, err := w.getHttpSvc().HttpPost(reqUrl, nil, data)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	// 调试日志
	slog.Debug("ConvertOpenIdToUserId:\n" + string(res.Body))

	type Result struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
		Userid  string `json:"userid"`
	}

	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return "", err
	}
	if result.Errcode != 0 {
		return "", errors.New("openid转userid失败，返回结果：" + string(res.Body))
	}
	return result.Userid, nil
}

// reloadJsApiTicket 获取企业的jsapi_ticket
// 生成签名之前必须先了解一下jsapi_ticket，jsapi_ticket是H5应用调用企业微信JS接口的临时票据。
// 正常情况下，jsapi_ticket的有效期为7200秒，通过access_token来获取。由于获取jsapi_ticket
// 的api调用次数非常有限（一小时内，一个企业最多可获取400次，且单个应用不能超过100次），频繁刷新
// jsapi_ticket会导致api调用受限，影响自身业务，开发者必须在自己的服务全局缓存jsapi_ticket。
func (w *QYWXApi) reloadJsApiTicket() (string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?" + values.Encode()

	// 调试
	//url += "&debug=1"

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	// 调试日志
	slog.Debug("reloadJsApiTicket", "response", string(res.Body))

	type Result struct {
		Errcode   int    `json:"errcode"`
		Errmsg    string `json:"errmsg"`
		Ticket    string `json:"ticket"`
		ExpiresIn int    `json:"expires_in"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return "", err
	}
	if len(result.Ticket) == 0 {
		return "", errors.New("获取accessToken失败，返回结果：" + string(res.Body))
	}

	// 缓存到redis中
	var ctx = context.Background()
	var cacheKey = "qywx_jsapi_ticket_" + w.CorpId
	cache.RedisClient.Set(ctx, cacheKey, result.Ticket, time.Duration(result.ExpiresIn-60)*time.Second)

	slog.Debug("qywx_jsapi_ticket: " + result.Ticket)
	return result.Ticket, nil
}

// GetJsApiTicket 获取企业的jsapi_ticket
func (w *QYWXApi) GetJsApiTicket() (string, error) {
	var ctx = context.Background()
	var cacheKey = "qywx_jsapi_ticket_" + w.CorpId
	ticket, err := cache.RedisClient.Get(ctx, cacheKey).Result()
	if err != nil || len(ticket) == 0 {
		ticket, err := w.reloadJsApiTicket()
		if err != nil {
			slog.Error(err.Error())
			return "", err
		} else {
			return ticket, nil
		}
	} else {
		return ticket, nil
	}
}

// reloadJsApiTicket 获取应用的jsapi_ticket
func (w *QYWXApi) reloadAgentJsApiTicket() (string, error) {
	accessToken, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}

	var values = url.Values{}
	values.Add("access_token", accessToken)
	values.Add("type", "agent_config")
	reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?" + values.Encode()

	// 调试
	//url += "&debug=1"

	res, err := w.getHttpSvc().HttpGet(reqUrl, nil)
	if err != nil {
		slog.Error(err.Error())
		return "", err
	}

	// 调试日志
	slog.Debug("reloadAgentJsApiTicket", "response", string(res.Body))

	type Result struct {
		Errcode   int    `json:"errcode"`
		Errmsg    string `json:"errmsg"`
		Ticket    string `json:"ticket"`
		ExpiresIn int    `json:"expires_in"`
	}
	var result Result
	if err := json.Unmarshal(res.Body, &result); err != nil {
		slog.Error(err.Error())
		return "", err
	}
	if len(result.Ticket) == 0 {
		return "", errors.New("获取accessToken失败，返回结果：" + string(res.Body))
	}

	// 缓存到redis中
	var ctx = context.Background()
	var cacheKey = "qywx_agent_jsapi_ticket_" + w.CorpId
	cache.RedisClient.Set(ctx, cacheKey, result.Ticket, time.Duration(result.ExpiresIn-60)*time.Second)

	slog.Debug("qywx_agent_jsapi_ticket: " + result.Ticket)
	return result.Ticket, nil
}

// GetAgentJsApiTicket 获取应用的jsapi_ticket
func (w *QYWXApi) GetAgentJsApiTicket() (string, error) {
	var ctx = context.Background()
	var cacheKey = "qywx_agent_jsapi_ticket_" + w.CorpId
	ticket, err := cache.RedisClient.Get(ctx, cacheKey).Result()
	if err != nil || len(ticket) == 0 {
		ticket, err := w.reloadAgentJsApiTicket()
		if err != nil {
			slog.Error(err.Error())
			return "", err
		} else {
			return ticket, nil
		}
	} else {
		return ticket, nil
	}
}
